/*
 *   This file is part of ed_pvr.
 *
 *   ed_pvr is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   ed_pvr is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with ed_pvr.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <memory.h>
#include <malloc.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <getopt.h>
#include "xmltv.h"
#include "utils.h"

#define MAX_XML_LEN						1024


#define	XPATH_CHANNEL_1					"/tv/channel"


#define	XPATH_CHANNEL_NAME				"/tv/channel[@id='%s']"
#define	XPATH_PROGRAMME_CHANNEL			"/tv/programme[@channel='%s']"
#define	XPATH_PROGRAMME_ID_START		"/tv/programme[@start='%s' and @channel='%s']"
#define	XPATH_PROGRAMME_ID_START_STOP	"/tv/programme[@start='%s' and @stop='%s' and @channel='%s']"
#define	XPATH_PROGRAMME_TITLE			"/tv/programme[@start='%s' and @channel='%s']/title"
#define	XPATH_PROGRAMME_STITLE			"/tv/programme[@start='%s' and @channel='%s']/sub-title"
#define	XPATH_PROGRAMME_CATEGORY		"/tv/programme[@start='%s' and @channel='%s']/category"
#define	XPATH_PROGRAMME_DESC			"/tv/programme[@start='%s' and @channel='%s']/desc"
#define	XPATH_PROGRAMME_PRATING			"/tv/programme[@start='%s' and @channel='%s']/rating"
#define	XPATH_PROGRAMME_SRATING			"/tv/programme[@start='%s' and @channel='%s']/star-rating"
#define	XPATH_PROGRAMME_INCOMPLETE		"/tv/programme[@start='%s' and @channel='%s']/%s"
#define	XPATH_PROGRAMME_EPISODE_NUM		"/tv/programme[@start='%s' and @stop='%s' and @channel='%s']/episode-num[@system='%s']"
//#define	XPATH_PROGRAMME_MATCH_EPISODE	"/tv/programme/episode-num[@system='%s']"
#define	XPATH_PROGRAMME_MATCH_EPISODE	"/tv/programme[episode-num='%s']/episode-num/.."


xmlNodePtr _xmltv_get_program_inc(XMLTV_STRUCT *, char *, char *, char*);

xmlXPathObjectPtr xmltv_getnodeset(xmlDocPtr doc, xmlChar *xpath)
{
	xmlXPathContextPtr context;
	xmlXPathObjectPtr result;

	context = xmlXPathNewContext(doc);
	if (context == NULL)
	{
		printf("Error in xmlXPathNewContext\n");
		return NULL;
	}
	result = xmlXPathEvalExpression(xpath, context);

	xmlXPathFreeContext(context);

	if (result == NULL)
	{
		printf("Error in xmlXPathEvalExpression\n");
		return NULL;
	}

	if(xmlXPathNodeSetIsEmpty(result->nodesetval))
	{
		xmlXPathFreeObject(result);
//		printf("No result\n");
		return NULL;
	}
	return result;
}

xmlChar *xmltv_get_content_from_ptr(xmlNodePtr tmp)
{
	xmlChar *ret = NULL;
	if (tmp)
			ret = xmlNodeGetContent(tmp);
	return ret;
}


xmlNodePtr _xmltv_get_program_inc(XMLTV_STRUCT *xmlstruct, char *id, char *start_time, char *str)
{
//	xmlChar *ret = NULL;
	xmlNodePtr ret = NULL;
	xmlChar tmpstr[MAX_XML_LEN] = {0};
	xmlXPathObjectPtr p = NULL;

	snprintf((char*)tmpstr, MAX_XML_LEN, XPATH_PROGRAMME_INCOMPLETE, start_time, id, str);
//	printf("inc: %s\n", tmpstr);
	p = xmltv_getnodeset (xmlstruct->doc, tmpstr);

	if (p)
	{
//		printf("xmltv_get_program_title good\n");
		xmlNodePtr tmp = p->nodesetval->nodeTab[0];
		if (tmp)
			ret = tmp;
		xmlXPathFreeObject(p);
	}
	else
	{
//		printf("xmltv_get_program_title sad\n");
	}
	return ret;
}


xmlNodePtr xmltv_get_channel(XMLTV_STRUCT *xmlstruct)
{
	xmlNodePtr ret = NULL;
	xmlChar tmpstr[MAX_XML_LEN] = {0};
	xmlXPathObjectPtr p = NULL;

	snprintf((char*)tmpstr, MAX_XML_LEN, XPATH_CHANNEL_1);
//	printf("inc: %s\n", tmpstr);
	p = xmltv_getnodeset (xmlstruct->doc, tmpstr);

	if (p)
	{
//		printf("xmltv_get_program_title good\n");
		xmlNodePtr tmp = p->nodesetval->nodeTab[0];
		if (tmp)
			ret = tmp;
		xmlXPathFreeObject(p);
	}
	else
	{
//		printf("xmltv_get_program_title sad\n");
	}
	return ret;


/*

	xmlChar tmpstr[MAX_XML_LEN] = {0};
	XMLTV_STRINGS_STRUCT * ret = calloc(1, sizeof(XMLTV_STRINGS_STRUCT));

	xmlXPathObjectPtr p = NULL;

	snprintf((char*)tmpstr, MAX_XML_LEN, XPATH_CHANNEL_1, id);
	p = xmltv_getnodeset (xmlstruct->doc, tmpstr);

	if (p)
	{
		int node_count = 0;
		xmlNodePtr tmp = p->nodesetval->nodeTab[0];
		node_count = p->nodesetval->nodeNr;
		tmp = tmp->children;
		while(tmp)
		{
			if (tmp->type ==  XML_ELEMENT_NODE)
			{
				if (xmlStrcmp(tmp->name, (xmlChar*)"display-name") == 0)
					ret->strs[ret->count++] = xmltv_get_content_from_ptr(tmp);
			}
			tmp = tmp->next;
		}
		xmlXPathFreeObject(p);
	}
	return ret;
*/
}

XMLTV_STRINGS_STRUCT * xmltv_get_channel_display(XMLTV_STRUCT *xmlstruct, char *id)
{
	xmlChar tmpstr[MAX_XML_LEN] = {0};
	XMLTV_STRINGS_STRUCT * ret = calloc(1, sizeof(XMLTV_STRINGS_STRUCT));

	xmlXPathObjectPtr p = NULL;

	snprintf((char*)tmpstr, MAX_XML_LEN, XPATH_CHANNEL_NAME, id);
	p = xmltv_getnodeset (xmlstruct->doc, tmpstr);

	if (p)
	{
//		int node_count = 0;
		xmlNodePtr tmp = p->nodesetval->nodeTab[0];
//		node_count = p->nodesetval->nodeNr;
		tmp = tmp->children;
		while(tmp)
		{
			if (tmp->type ==  XML_ELEMENT_NODE)
			{
				if (xmlStrcmp(tmp->name, (xmlChar*)"display-name") == 0)
					ret->strs[ret->count++] = xmltv_get_content_from_ptr(tmp);
			}
			tmp = tmp->next;
		}
		xmlXPathFreeObject(p);
	}
	return ret;
}


xmlChar *xmltv_get_program_title(XMLTV_STRUCT *xmlstruct, char *id, char *start_time)
{
	xmlChar *ret = NULL;
//	xmlChar tmpstr[MAX_XML_LEN] = {0};

	xmlNodePtr tmp = _xmltv_get_program_inc(xmlstruct, id, start_time, "title");
	if (tmp)
			ret = xmlNodeGetContent(tmp);
	return ret;
}

xmlChar *xmltv_get_program_subtitle(XMLTV_STRUCT *xmlstruct, char *id, char *start_time)
{
	xmlChar *ret = NULL;
//	xmlChar tmpstr[MAX_XML_LEN] = {0};
	xmlNodePtr tmp = _xmltv_get_program_inc(xmlstruct, id, start_time, "sub-title");
	if (tmp)
			ret = xmlNodeGetContent(tmp);
	return ret;
}

xmlChar *xmltv_get_program_category(XMLTV_STRUCT *xmlstruct, char *id, char *start_time)
{
	xmlChar *ret = NULL;
//	xmlChar tmpstr[MAX_XML_LEN] = {0};
	xmlNodePtr tmp = _xmltv_get_program_inc(xmlstruct, id, start_time, "category");
	if (tmp)
			ret = xmlNodeGetContent(tmp);
	return ret;
}

xmlChar *xmltv_get_program_desc(XMLTV_STRUCT *xmlstruct, char *id, char *start_time)
{
	xmlChar *ret = NULL;
//	xmlChar tmpstr[MAX_XML_LEN] = {0};
	xmlNodePtr tmp = _xmltv_get_program_inc(xmlstruct, id, start_time, "desc");
	if (tmp)
			ret = xmlNodeGetContent(tmp);
	return ret;
}

xmlNodePtr xmltv_get_id_and_times(XMLTV_STRUCT *xmlstruct, char *id, char *start_time, char *end_time)
{
	xmlNodePtr ret = NULL;
	xmlChar tmpstr[MAX_XML_LEN] = {0};
	xmlXPathObjectPtr p = NULL;

	if (end_time)
		snprintf((char*)tmpstr, MAX_XML_LEN, XPATH_PROGRAMME_ID_START_STOP, start_time, end_time, id);
	else
		snprintf((char*)tmpstr, MAX_XML_LEN, XPATH_PROGRAMME_ID_START, start_time, id);
//	printf("prg_p: %s\n", tmpstr);
	p = xmltv_getnodeset(xmlstruct->doc, tmpstr);

	if (p)
	{
//		printf("xmltv_get_program_info good\n");
		xmlNodePtr tmp = p->nodesetval->nodeTab[0];
		if (tmp)
			ret = tmp;
		xmlXPathFreeObject(p);
	}
	else
	{
//		printf("xmltv_get_id_start_stop sad\n");
	}
	return ret;
}

TIMER_STRUCT * xmltv_get_chan_list(XMLTV_STRUCT *xmlstruct, char *id)
{
//	TIMER_STRUCT * ret = NULL, *current = NULL;
	return NULL;
}
TIMER_STRUCT * xmltv_get_epg_list(XMLTV_STRUCT *xmlstruct, char *id)
{
	TIMER_STRUCT * ret = NULL, *current = NULL;
	int node_count = 0;
	xmlChar tmpstr[MAX_XML_LEN] = {0};
	xmlXPathObjectPtr p = NULL;

	snprintf((char*)tmpstr, MAX_XML_LEN, XPATH_PROGRAMME_CHANNEL, id);
	p = xmltv_getnodeset(xmlstruct->doc, tmpstr);

	if (p)
	{
		int i;
		node_count = p->nodesetval->nodeNr;
//		printf("xmltv_get_epg_list, count: %d\n", node_count);

		for(i=0; i < node_count; i++)
		{
			xmlNodePtr tmp = p->nodesetval->nodeTab[i];
			xmlNodePtr child = tmp->children;
			if (tmp && child)
			{
//				uint8_t use_extra = 0;
				TIMER_STRUCT * tmp_timer = calloc(1, sizeof(*tmp_timer));
				TIMER_STRUCT_EXTRA * extra = calloc(1, sizeof(*extra));

				xmlChar * start = xmlGetProp(tmp, (xmlChar *)"start");
				xmlChar * stop = xmlGetProp(tmp, (xmlChar *)"stop");
				tmp_timer->extra_data = (void*)extra;

				if (start)
				{
					tmp_timer->data.start_epoch = utils_str_to_epoch((char*)start);
//					printf("Start: %s (%" PRId64")\n", start, tmp_timer->data.start_epoch);
					xmlFree(start);
				}

				if (stop)
				{
					tmp_timer->data.end_epoch = utils_str_to_epoch((char*)stop);
//					printf("Stop: %s (%" PRId64")\n", stop, tmp_timer->data.end_epoch);
					xmlFree(stop);
				}

//				printf("Start: %" PRId64" -- Stop: %"PRId64"\n", tmp_timer->data.start_epoch, tmp_timer->data.end_epoch);
				while(child)
				{
					if (child->type ==  XML_ELEMENT_NODE)
					{
						xmlChar * s = xmltv_get_content_from_ptr(child);

						if (xmlStrcmp(child->name, (xmlChar *)"title") == 0)
						{
//							printf("\tTitle: %s\n", s);
							strncpy(tmp_timer->data.title, (char*)s, TIEMR_STRUCT_MAX_STRLEN-1);
						}
						else if (xmlStrcmp(child->name, (xmlChar *)"sub-title") == 0)
						{
//							printf("\tsub-title: %s\n", s);
							strncpy(tmp_timer->data.plot_outline, (char*)s, TIEMR_STRUCT_MAX_STRLEN-1);
						}
						else if (xmlStrcmp(child->name, (xmlChar *)"desc") == 0)
						{
//							printf("\tDesc: %s\n", s);
							strncpy(tmp_timer->data.plot, (char*)s, TIEMR_STRUCT_MAX_STRLEN-1);
						}
						else if (extra && child->name)
						{
							if (xmlStrcmp(child->name, (xmlChar *)"date") == 0)
							{
								extra->org_air_date = utils_date_to_epoch((char*)s);
//								printf("Org Date: %" PRId64"\n", extra->org_air_date);
							}
							else if (xmlStrcmp(child->name, (xmlChar *)"category") == 0)
							{
								if (extra->category_count < TIMER_EXTRA_CAT_MAX)
								{
									if ((xmlStrcmp(s, (xmlChar *)"Series") != 0) &&
										(xmlStrcmp(s, (xmlChar *)"Movies") != 0) &&
										(xmlStrcmp(s, (xmlChar *)"Outdoors") != 0) &&
										(xmlStrcmp(s, (xmlChar *)"Kids") != 0))
										strncpy(extra->category[extra->category_count++], (char*)s, TIEMR_STRUCT_MAX_STRLEN-1);
								}
//								else
//									printf("BAAAAAAA\n");
							}
						}

	//					printf("[%d] [%s] MOO: %s\n", child->type, child->name, s);
						if (s)
							xmlFree(s);
					}
					child = child->next;
				}
/*
				if (use_extra == 0)
				{
					free(extra);
					tmp_timer->extra_data = NULL;
				}
*/
				if (ret == NULL)
				{
					ret = current = tmp_timer;
					extra->epg_count = node_count;
				}
				else
				{
					current->next = tmp_timer;
					current = tmp_timer;
				}
			}
		}
/*
		if (current)
		{
			TIMER_STRUCT_EXTRA * extra = current->extra_data;
//			extra->last = 1;
		}
*/
		xmlXPathFreeObject(p);
	}
	else
	{
//		printf("xmltv_get_id_start_stop sad\n");
	}
//	printf("End\n");
	return ret;
}


xmlNodePtr xmltv_get_episode_dd_nodeptr(XMLTV_STRUCT *xmlstruct, char *dd)
{
	xmlNodePtr ret = NULL;
	xmlChar tmpstr[MAX_XML_LEN] = {0};
	xmlXPathObjectPtr p = NULL;


	snprintf((char*)tmpstr, MAX_XML_LEN, XPATH_PROGRAMME_MATCH_EPISODE, dd);
//	printf("prg_p: %s\n", tmpstr);
	p = xmltv_getnodeset(xmlstruct->doc, tmpstr);

	if (p)
	{
		xmlNodePtr tmp = p->nodesetval->nodeTab[0];
		if (tmp)
			ret = tmp;
		xmlXPathFreeObject(p);
	}
	return ret;
}

xmlChar * xmltv_get_episode_dd(XMLTV_STRUCT *xmlstruct, char *id, char *start_time, char *end_time)
{
	xmlChar * ret = NULL;
	xmlChar tmpstr[MAX_XML_LEN] = {0};
	xmlXPathObjectPtr p = NULL;


	snprintf((char *)tmpstr, MAX_XML_LEN, XPATH_PROGRAMME_EPISODE_NUM, start_time, end_time, id, "dd_progid");
//	printf("prg_p: %s\n", tmpstr);
	p = xmltv_getnodeset(xmlstruct->doc, tmpstr);

	if (p)
	{
		xmlNodePtr tmp = p->nodesetval->nodeTab[0];
		if (tmp)
			ret = xmlNodeGetContent(tmp);
		xmlXPathFreeObject(p);
	}
	return ret;
}


void xmltv_cleanup(XMLTV_STRUCT * x)
{
	if (x)
	{
		if (x->doc)
			xmlFreeDoc(x->doc);

		if (x->filename)
            free(x->filename);

        free(x);
		malloc_trim(0);
	}
}

XMLTV_STRUCT * xmltv_init(char *fname)
{
	if (fname)
	{
//		printf("xmltv_init 1\n");
		XMLTV_STRUCT *ret = calloc(1, sizeof(XMLTV_STRUCT));
		if (ret)
		{
//		printf("xmltv_init 2\n");
			ret->filename = strdup(fname);
//		printf("xmltv_init 3: %s\n", ret->filename);
			malloc_trim(0);
//			ret->doc = xmlParseFile(ret->filename);
			ret->doc = xmlReadFile(ret->filename, NULL, 0);
			malloc_trim(0);
//			ret->doc = xmlSAXParseFile(NULL, ret->filename, 0);
//		printf("xmltv_init 4\n");

			if (!ret->doc)
			{
				if (ret->filename)
					free(ret->filename);
				free(ret);
				ret = NULL;
			}
			else
				stat(fname, &ret->file_stat);
		}
		return ret;
	}
	return NULL;
}


#ifdef _MAIN_

char *input_file = NULL;
int chan = 0;

static const char *optString = "i:a:";

static const struct option longOpts[] =
{
//    { "input", required_argument, NULL, 'i' },
    { NULL, no_argument, NULL, 0 }
};

uint8_t get_opts(int argc, char *const argv[])
{
	int index = 0;
	int opt = 0;

	do
	{
		opt = getopt_long(argc, argv, optString, longOpts, &index);
		switch (opt)
		{
			case 'i':
				if (optarg)
					input_file = strdup(optarg);
			break;

			case 'a':
				if (optarg)
					chan = atoi(optarg);
			break;

			default:
			break;
		}
	}
	while (opt != -1);

	return 0;
}


int main(int argc, char *argv[])
{
	char tmp[255] = {0};
	XMLTV_STRUCT * x = NULL;
	if (get_opts(argc, argv) || !input_file)
	{
		printf("Need args -i\n");
		exit(1);
	}
//	printf("CHAN: %d\n", chan);
	x = xmltv_init(input_file);
//	printf("MOO: %s\n", tmp);
	if (x)
	{
		int i;

		FILE *fp = fopen("/root/chans.txt", "r");
		if (fp)
		{
			xmlChar *id = NULL;
			xmlNodePtr ptr = NULL, ptr2 = NULL;
			int chan_num = 0;

			ptr =xmltv_get_channel(x);
			while (ptr)
			{
				char * s= NULL, *e = NULL, *c = NULL;
				s =  (char*)xmlGetProp(ptr, (xmlChar *)"id");
				if (s)
				{
					int count = 0;
					ptr2 = ptr->children;
//					printf("YAY! [%s]\n", s);
					while (ptr2)
					{
						if (ptr2->type == XML_ELEMENT_NODE)
						{
							count++;
							if (count == 3)
							{
//								printf("%s,%s,empty\n", xmltv_get_content_from_ptr(ptr2), s);
								printf("%s,%s,http://fileserver2/xmltv/icons/%s.png\n", xmltv_get_content_from_ptr(ptr2), s, s);
							}
						}
						ptr2 = ptr2->next;
					}

					free(s);
				}
				ptr = ptr->next;
			}
/*
			else
			{
				printf("NOO!\n");
			}
*/
/*
			while(fscanf(fp, "%d", &chan_num) != EOF)
			{
//				sprintf(tmp, "I%d.labs.zap2it.com", chan_num);
				sprintf(tmp, "I%d.*.microsoft.com", chan_num);
				XMLTV_STRINGS_STRUCT * str = xmltv_get_channel_display(x, tmp);
				if (str)
				{
					id = str->strs[0];
					if (str->count == 5)
					{
						if (strstr((const char *)str->strs[3], "(") ==  NULL)
							id = str->strs[3];
					}


					printf("Name [%d]: %s [%s]\n", str->count, id, tmp);
				}
			}
*/
			fclose(fp);
		}

/*
			for(i=0; i < str->count; i++)
				printf("[%d] HI: %s\n", i, str->strs[i]);
*/
//		xmlChar * tmp = xmltv_get_channel_display(x, "I802.labs.zap2it.com");
/*
		TIMER_STRUCT * timer = xmltv_get_epg_list(x, "I802.labs.zap2it.com");
		TIMER_STRUCT_EXTRA *extra = (TIMER_STRUCT_EXTRA *)timer->extra_data;
		printf("EPG COUNT: %" PRId64"\n", extra->epg_count);
		while (timer)
		{
			extra = (TIMER_STRUCT_EXTRA *)timer->extra_data;
			printf("Title: %s, Start: %" PRId64", Stop: %" PRId64"\n",  timer->title, timer->start_epoch, timer->end_epoch);
			printf("\t%s\n", timer->plot_outline);
			printf("\t%s\n", timer->plot);
			printf("\t%" PRId64"\n", extra->org_air_date);

			timer = timer->next;
		}

*/


/*
		char *start = utils_epoch_to_str(1364013000);
		char *end = utils_epoch_to_str(1364020200);

		xmlNodePtr tmpptr = xmltv_get_id_and_times(x, "I802.labs.zap2it.com", start, end);
		xmlNodePtr tmpptr = xmltv_get_episode_dd_nodeptr(x, "EP00815061.0002");
		if (tmpptr)
		{
			char * s= NULL, *e = NULL, *c = NULL;
//			char *moo = tmpptr->parent->name;
			char *moo = tmpptr->name;
			s =  xmlGetProp(tmpptr, "start");
			e =  xmlGetProp(tmpptr, "stop");
			c =  xmlGetProp(tmpptr, "channel");
			printf("yay1: %s -- %s %s %s\n", moo, s, e, c);
		}
		else
			printf("no1\n");
		printf("parsed\n");
*/

		xmltv_cleanup(x);
	}

	return 0;
}
#endif
